#include "stdafx.h"
#include "Log.h"
#include <string>
#include <sstream>
#include <log4cpp/Portability.hh>
#include <log4cpp/Category.hh>
#include <log4cpp/PropertyConfigurator.hh>
#include <strsafe.h>
#include "log4cpp/NDC.hh"

static std::string errorMessage = "";

std::wstring A2W(const std::string& str) {
	int widecharsLen = ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.size(), NULL, 0);
	if (widecharsLen <= 0)
		return L"";

	std::wstring retStr(widecharsLen, 0);
	::MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.size(), &retStr[0], (int)retStr.length());

	return retStr;
}

std::string W2A(const std::wstring& str) {
	int multiCharsLen = ::WideCharToMultiByte(CP_ACP, 0, str.c_str(), str.size(), NULL, 0, NULL, NULL);
	if (multiCharsLen <= 0)
		return "";

	std::string retStr(multiCharsLen, 0);
	::WideCharToMultiByte(CP_ACP, 0, str.c_str(), str.size(), &retStr[0], (int)retStr.length(), NULL, NULL);

	return retStr;
}

void log(log4cpp::Category& category, int level, const std::string message) {
	category.log(level, message);
}

std::string getLogMessageA(const char* fmt, va_list valist) {
	std::vector<char> bufferVec(1024, 0);
	while (1) {
		va_list args_copy;
		args_copy = valist;

		int n = _vsnprintf(&bufferVec[0], bufferVec.size(), fmt, args_copy);

		va_end(args_copy);

		// If that worked, return a string.
		if ((n > -1) && (static_cast<size_t>(n) < bufferVec.size())) {
			bufferVec.resize(n);
			return std::string(bufferVec.begin(), bufferVec.end());
		}

		// Else try again with more space.
		if (n > -1) 
			bufferVec.resize(n + 1,0);
		else 
			bufferVec.resize(bufferVec.size() * 2); // twice the old size
	}
}

std::wstring getLogMessageW(const wchar_t* fmt, va_list valist) {
	std::vector<wchar_t> bufferVec(1024, 0);
	while (1) {
		va_list args_copy;
		args_copy = valist;

		int n = _vsnwprintf(&bufferVec[0], bufferVec.size(), fmt, args_copy);

		va_end(args_copy);

		// If that worked, return a string.
		if ((n > -1) && (static_cast<size_t>(n) < bufferVec.size())) {
			bufferVec.resize(n);
			return std::wstring(bufferVec.begin(), bufferVec.end());
		}

		// Else try again with more space.
		if (n > -1) 
			bufferVec.resize(n + 1,0);
		else 
			bufferVec.resize(bufferVec.size() * 2); // twice the old size
	}
}


bool ArcInitializeLogSystemA(const char* config) {
	
	try {
		log4cpp::PropertyConfigurator::configure(config);
	} catch(log4cpp::ConfigureFailure& f) {
		std::stringstream strstream;
		strstream << "Configure Problem " << f.what() << std::endl;
		errorMessage = strstream.str();
		return false;
	}

	return true;
}

bool ArcInitializeLogSystemW(const wchar_t* config){
	
	try {
		log4cpp::PropertyConfigurator::configure(W2A(config));
	} catch(log4cpp::ConfigureFailure& f) {
		std::stringstream strstream;
		strstream << "Configure Problem " << f.what() << std::endl;
		errorMessage = strstream.str();
		return false;
	}

	return true;
}

void ArcUnInitializeLogSystemA() {
	errorMessage = "";
	log4cpp::Category::shutdown();
}

void ArcLogA(int level, const char* fmt, ...) {
	log4cpp::Category& defualtCategory = log4cpp::Category::getRoot();
	
	va_list va;
	va_start(va, fmt);
	log(defualtCategory, level, getLogMessageA(fmt, va));
	va_end(va);
}

void ArcLogW(int level, const wchar_t* fmt, ...) {
	log4cpp::Category& defualtCategory = log4cpp::Category::getRoot();

	va_list va;
	va_start(va, fmt);
	std::wstring logMessage = getLogMessageW(fmt, va);
	log(defualtCategory, level, W2A(logMessage));
	va_end(va);
}

void ArcCategoryLogA(int level, const char* category_name, const char* fmt, ...) {

	 log4cpp::Category& category = log4cpp::Category::getInstance(category_name);

	 va_list va;
	 va_start(va, fmt);
	 log(category, level, getLogMessageA(fmt, va));
	 va_end(va);
}

void ArcCategoryLogW(int level, const wchar_t* category_name, const wchar_t* fmt, ...) {
	log4cpp::Category& category = log4cpp::Category::getInstance(W2A(category_name));

	va_list va;
	va_start(va, fmt);
	std::wstring logMessage = getLogMessageW(fmt, va);
	log(category, level, W2A(logMessage));
	va_end(va);
}

int ArcLogGetErrorMessageW(wchar_t* errorMsgBuffer, int messageLen) {
	std::wstring errorMessageW = A2W(errorMessage);
	StringCbCopyW(errorMsgBuffer, messageLen, errorMessageW.c_str());
	return (int)(errorMessageW.size()) + 1;
}

int ArcLogGetErrorMessageA(char* errorMsgBuffer, int messageLen) {
	StringCbCopyA(errorMsgBuffer, messageLen, errorMessage.c_str());
	return (int)(errorMessage.size()) + 1;
}

void ArcLogNDCPushA(const char* ndcmessage){
	log4cpp::NDC::push(ndcmessage);
}

void ArcLogNDCPushW(const wchar_t* ndcmessage) {
	log4cpp::NDC::push(W2A(ndcmessage));
}

void ArcLogNDCPop() {
	log4cpp::NDC::pop();
}